package com.qcby.aspect;


import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.CodeSignature;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.util.StopWatch;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.servlet.mvc.method.RequestMappingInfo;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.lang.reflect.Method;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 打印args方面
 *
 * @author cong.zhen
 * @date 2023/12/12
 */
@Aspect
@Slf4j
public class PrintArgsAspect implements InitializingBean, ApplicationContextAware {

    private final Set<String> ignoreArgs = new HashSet();
    private final PrintArgsProperties printArgsProperties;
    private final ObjectMapper objectMapper;
    private ApplicationContext applicationContext;

    public PrintArgsAspect(PrintArgsProperties printArgsProperties) {
        this.printArgsProperties = printArgsProperties;
        this.objectMapper = new ObjectMapper();
        this.objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
        this.objectMapper.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false);
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        this.ignoreArgs.add(HttpServletRequest.class.getCanonicalName());
        this.ignoreArgs.add(HttpServletResponse.class.getCanonicalName());
        this.ignoreArgs.add(MultipartFile.class.getCanonicalName());
        this.printArgsProperties.getIgnoreArgsType().forEach((e) -> {
            this.ignoreArgs.add(e);
        });
    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) {
        this.applicationContext = applicationContext;
    }

    @Pointcut("@within(org.springframework.web.bind.annotation.RestController)")
    public void restController() {
    }

    @Pointcut("@within(org.springframework.stereotype.Controller)")
    public void controller() {
    }

    @Around("restController() || controller()")
    public Object invokeResource(ProceedingJoinPoint point) throws Throwable {
        Object[] paramValues = point.getArgs();
        String[] paramNames = ((CodeSignature)point.getSignature()).getParameterNames();
        Class[] parameterTypes = ((CodeSignature)point.getSignature()).getParameterTypes();
        List<Map<String, Object>> precondition = new ArrayList();

        String response;
        for(int i = 0; i < parameterTypes.length; ++i) {
            response = paramNames[i];
            Object value = "Ignore";
            if (!this.ignoreArgs.contains(parameterTypes[i])) {
                value = paramValues[i];
            }

            Map<String, Object> singleArgs = new HashMap(16);
            singleArgs.put(response, value);
            precondition.add(singleArgs);
        }

        String request = this.transform(precondition);
        response = "N/A";
        boolean exception = false;
        StopWatch stopWatch = new StopWatch("execute-api");
        stopWatch.start();

        Object var11;
        try {
            Object result = point.proceed();
            response = this.transform(result);
            var11 = result;
        } catch (Exception var15) {
            exception = true;
            throw var15;
        } finally {
            stopWatch.stop();
            log.info(this.printArgsProperties.getTemplate(), new Object[]{this.getUrlList(point), request, response, exception, stopWatch.getTotalTimeMillis()});
        }

        return var11;
    }

    private String transform(Object obj) {
        if (Objects.isNull(obj)) {
            return "N/A";
        } else {
            String args;
            if (this.printArgsProperties.getPrintType() == PrintType.JSON) {
                try {
                    args = this.objectMapper.writeValueAsString(obj);
                } catch (Exception var4) {
                    args = "N/A";
                }
            } else {
                args = String.valueOf(obj);
            }

            return args.length() > this.printArgsProperties.getPrintLength() ? args.substring(0, this.printArgsProperties.getPrintLength()) : args;
        }
    }

    private String getUrlList(ProceedingJoinPoint point) {
        List<String> urls = new ArrayList();
        Method target = this.getTargetMethod(point);

        try {
            RequestMappingHandlerMapping handlerMapping = (RequestMappingHandlerMapping)this.applicationContext.getBean(RequestMappingHandlerMapping.class);
            Map<RequestMappingInfo, HandlerMethod> handlerMethods = handlerMapping.getHandlerMethods();
            urls = (List)handlerMethods.entrySet().stream().filter((e) -> {
                return ((HandlerMethod)e.getValue()).getMethod().equals(target);
            }).map((e) -> {
                return ((RequestMappingInfo)e.getKey()).getPatternsCondition().getPatterns();
            }).flatMap(Collection::stream).collect(Collectors.toList());
        } catch (Exception var6) {
        }

        return String.join(",", (Iterable)urls);
    }

    private Method getTargetMethod(ProceedingJoinPoint point) {
        try {
            Method targetMethod = ((MethodSignature)((MethodSignature)point.getSignature())).getMethod();
            return targetMethod;
        } catch (Exception var3) {
            return null;
        }
    }
}
